home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9310.ZIP
/
ALLHELP.ZIP
/
HELPDUMP.C
next >
Wrap
Text File
|
1993-08-01
|
41KB
|
1,235 lines
/***************************************************
HLPDUMP1.C
Peter Davis - 05/93
Taken from the original HELPDUMP.C written by
Ron Burk.
Dumps known files from WinHelp internal file
system in a formatted output. If WHIFS file is
unknown, hex/ascii dump of file is taken.
HELPDUMP.C will replace HLPDUMP1.C with extended
functionality for handling more WHIFS files.
****************************************************/
/*************************************************
If it's not Turbo C, assume MSC 6.0 As for the
rest of you, you'll have to customize. Sorry.
**************************************************/
#pragma pack(1) /* Make sure we get byte alignment */
#ifndef __TURBOC__
#include <graph.h>
#define clrscr() _clearscreen(_GCLEARSCREEN);
#endif
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
#include <limits.h>
#include "whstruct.h"
#include "helpdump.h"
HELPHEADER HelpHeader; /* Header for Help file. */
WHIFSBTREEHEADER WHIFSHeader; /* WHIFS Header record */
int WHIFSLeafOne = -1; /* First WHIFS Leaf Node */
long FirstPageLoc; /* Used by macros for b-trees */
char WHIFSFileToRead[19]; /* Internal file to dump */
int ReadWHIFSFile; /* Flag to dump an internal file */
SYSTEMHEADER SysHeader; /* Global System Header Record */
char *PhrasesPtr;
int TopicUse;
/***************************************************
Finds the first leaf in the WHIFS B-Tree
****************************************************/
void WHIFSGetFirstLeaf(FILE *HelpFile) {
int CurrLevel = 1; /* Current Level in B-Tree */
BTREEINDEXHEADER CurrNode; /* Current Node in B-Tree */
int NextPage = 0; /* Next Page to go to */
/* Go to the beginning of WHIFS B-Tree */
fseek(HelpFile, HelpHeader.WHIFS, SEEK_SET);
fread(&WHIFSHeader, sizeof(WHIFSHeader), 1, HelpFile);
FirstPageLoc = HelpHeader.WHIFS + sizeof(WHIFSHeader);
/* Find First Leaf */
while (CurrLevel < WHIFSHeader.NLevels) {
fread(&CurrNode, sizeof(CurrNode), 1, HelpFile);
/* Next Page is conveniently the first byte of the page */
fread(&NextPage, sizeof(int), 1, HelpFile);
GotoWHIFSPage(NextPage);
CurrLevel++;
}
/* First Leaf page is here */
WHIFSLeafOne = NextPage;
}
/***************************************************
Gets a particular file by file number.
Needs the root node of the tree.
Returns the offset of the file and the filename.
****************************************************/
void GetFile(FILE *HelpFile, int FileNumber, long *FileOffset, char *FileName) {
BTREENODEHEADER CurrentNode;
int CurrPage; /* Keep track of page # */
int Index, counter = 0;
char c, TempFile[19];
/* Skip pages we don't need */
CurrentNode.NextPage = WHIFSLeafOne;
do {
CurrPage = CurrentNode.NextPage;
GotoWHIFSPage(CurrPage);
fread(&CurrentNode, sizeof(CurrentNode), 1, HelpFile);
counter += CurrentNode.NEntries;
} while (counter < FileNumber);
for (counter -= CurrentNode.NEntries; counter <= FileNumber; counter++) {
Index = 0;
while(c = fgetc(HelpFile))
TempFile[Index++] = c;
TempFile[Index] = 0; /* End of line */
fread(FileOffset, sizeof(long), 1, HelpFile);
}
strcpy(FileName, TempFile);
}
/***************************************************
Loads the SysHeader into memory. Need this later
on to determine if compression is used on help file.
****************************************************/
void SysLoad(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
}
/*************************************************
Returns a 1 if the bit is set, else 0 returned
**************************************************/
int BitSet(BYTE BitMap, int Bit) {
if (BitMap & (1<<Bit)) return 1;
else return 0;
}
/*************************************************
Decides how many bytes to read, depending on the
number of bits set in the Bitmap
**************************************************/
int BytesToRead(BYTE BitMap) {
int TempSum, counter;
TempSum = 8;
for (counter = 0; counter < 8; counter ++)
TempSum += BitSet(BitMap, counter);
return TempSum;
}
/*************************************************
Decompresses the data using Microsoft's LZ77
derivative.
**************************************************/
WORD Decompress(FILE *HelpFile, WORD CompSize, char *Buffer) {
WORD InBytes = 0; /* How many bytes read in */
WORD OutBytes = 0; /* How many bytes written out */
BYTE BitMap, Set[16]; /* Bitmap and bytes associated with it */
int NumToRead; /* Number of bytes to read for next group */
int counter, Index; /* Going through next 8-16 codes or chars */
int Length, Distance; /* Code length and distance back in 'window' */
char *CurrPos; /* Where we are at any given moment */
char *CodePtr; /* Pointer to back-up in LZ77 'window' */
CurrPos = Buffer;
/* Go through until we're done */
while (InBytes < CompSize) {
/* Get BitMap and data following it */
BitMap = fgetc(HelpFile);
NumToRead = BytesToRead(BitMap);
/* If we're trying to read more than we've got left, only read what we have left. */
NumToRead = (CompSize - InBytes) < NumToRead ? CompSize-InBytes : NumToRead;
fread(Set, 1, NumToRead, HelpFile);
InBytes += NumToRead + 1;
/* Go through and decode data */
for (counter = 0, Index = 0; counter < 8; counter++) {
/* It's a code, so decode it and copy the data */
if (BitSet(BitMap, counter)) {
Length = ((Set[Index+1] & 0xF0) >> 4) + 3;
Distance = (256 * (Set[Index+1] & 0x0F)) + Set[Index] + 1;
CodePtr = CurrPos - Distance;
/* Copy data from 'window' */
while (Length) {
*CurrPos++ = *CodePtr++;
OutBytes++;
Length--;
}
Index += 2;
} /* if */
else {
*CurrPos++ = Set[Index++];
OutBytes++;
}
} /* for */
} /* while */
return OutBytes;
}
/***************************************************
List the WHIFS directory to the screen.
****************************************************/
void ListFiles(FILE *HelpFile) {
DWORD FileCounter;
char FileName[20];
long FileOffset;
/* Load WHIFS Header and Get first leaf */
WHIFSGetFirstLeaf(HelpFile);
for (FileCounter = 0;
FileCounter < WHIFSHeader.TotalWHIFSEntries;
FileCounter++) {
GetFile(HelpFile, FileCounter, &FileOffset, FileName);
printf("File:%-10s FileOffset:0x%08lX ",FileName, FileOffset);
/* Make it double column */
if ((FileCounter % 2) != 0) printf("\n");
else printf("|");
}
}
/***************************************************
Performs a Hex/ASCII dump of a WHIFS file.
****************************************************/
void HexDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
char Buffer[16];
long counter;
int BytesToPrint, Index;
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
printf("File Size: 0x%08lX\n\n", FileHdr.FileSize);
printf("Offset Hex Values Ascii\n");
printf("-------------------------------------------------------------------------\n");
for (counter = 0; counter < FileHdr.FileSize; counter+=16) {
printf("0x%08lX: ", counter);
/* If this is the last line, how many bytes are in it? */
BytesToPrint = ((FileHdr.FileSize - counter) > 16) ? 16 : FileHdr.FileSize - counter;
fread(Buffer, BytesToPrint, 1, HelpFile);
/* Dump Hex */
for (Index=0; Index < BytesToPrint; Index++)
printf("%02X ", (BYTE) Buffer[Index]);
/* If last line, fill in blanks */
for (Index=0; Index < 16-BytesToPrint; Index++)
printf(" ");
/* Dump Ascii */
for (Index=0; Index < BytesToPrint; Index++)
putchar( isprint( Buffer[Index] ) ? Buffer[Index] : '.' );
putchar('\n');
}
free(Buffer);
}
/***************************************************
Display information about the |SYSTEM file.
****************************************************/
void SysDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
char HelpFileTitle[33];
SYSTEMREC SystemRec;
WORD CurrentLocation;
struct tm *TimeRec;
SECWINDOW *SWin; /* Secondary Window record */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
printf("|SYSTEM Dump\n\n\n");
/* Figure out Version and Revision */
if (SysHeader.Revision == 0x0F) printf("HC.EXE 3.00 Help Compiler used\n");
else if (SysHeader.Revision == 0x15) printf("HC.EXE 3.10 Help Compiler used\n");
else if (SysHeader.Revision == 0x21) printf("MVC.EXE Multimedia Compiler used.\n");
else printf("Unknown Compiler used!\n");
printf("\nVersion: %d\nRevision: %d\n", SysHeader.Version, SysHeader.Revision);
printf("Flag: 0x%04X - ",SysHeader.Flags);
/* Determine compression, if any. */
if (SysHeader.Flags == 0x00) printf("No compression\n");
else if (SysHeader.Flags & COMPRESSION_310) printf("Compressed\n");
else if (SysHeader.Flags & COMPRESSION_UNKN) printf("Compressed\n");
else printf("Unknown\n");
TimeRec=localtime(&SysHeader.GenDate);
printf("Help File Generated: %s", asctime(TimeRec));
/* If 3.0 get title */
CurrentLocation=12;
if (SysHeader.Revision == 0x0F) {
fgets(HelpFileTitle, 33, HelpFile);
printf("Help File Title: %s\n", HelpFileTitle);
}
/* Else, get 3.1 System records */
else {
while (CurrentLocation < FileHdr.FileSize) {
/* Read in system record and SystemRec data */
fread(&SystemRec, 4, 1, HelpFile);
SystemRec.RData = malloc(SystemRec.DataSize);
fread(SystemRec.RData, SystemRec.DataSize, 1, HelpFile);
CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
switch(SystemRec.RecordType) {
case 0x0001: printf("Help File Title: %s\n", SystemRec.RData);
break;
case 0x0002: printf("Copyright Notice: %s\n", SystemRec.RData);
break;
case 0x0003: printf("Contents ID: 0x%04X\n", (long) *SystemRec.RData);
break;
case 0x0004: printf("Macro Data: %s\n",SystemRec.RData);
break;
case 0x0005: printf("Icon in System record\n");
break;
case 0x0006: printf("\nSecondary window:\n");
SWin = (SECWINDOW *)SystemRec.RData;
printf("Flag: %d\n", SWin->Flags);
if (SWin->Flags & WSYSFLAG_TYPE)
printf("Type: %s\n", SWin->Type);
if (SWin->Flags & WSYSFLAG_NAME)
printf("Name: %s\n", SWin->Name);
if (SWin->Flags & WSYSFLAG_CAPTION)
printf("Caption: %s\n", SWin->Caption);
if (SWin->Flags & WSYSFLAG_X)
printf("X: %d\n", SWin->X);
if (SWin->Flags & WSYSFLAG_Y)
printf("Y: %d\n", SWin->Y);
if (SWin->Flags & WSYSFLAG_WIDTH)
printf("Width: %d\n", SWin->Width);
if (SWin->Flags & WSYSFLAG_HEIGHT)
printf("Height: %d\n", SWin->Height);
if (SWin->Flags & WSYSFLAG_MAXIMIZE)
printf("Maximize Flag: %d\n", SWin->Maximize);
if (SWin->Flags & WSYSFLAG_RGB)
printf("RGB Foreground Colors Set\n");
if (SWin->Flags & WSYSFLAG_RGBNSR)
printf("RGB For Non-Scrollable Region Set\n");
if (SWin->Flags & WSYSFLAG_TOP)
printf("Secondary Window is always On Top\n");
break;
case 0x0008: printf("Citation: %s\n", SystemRec.RData);
break;
default: printf("Unknown record type: 0x%04X\n",SystemRec.RecordType);
} /* switch */
free(SystemRec.RData);
} /* while */
} /* else */
} /* SysDump */
/***************************************************
Prints a Phrase from the Phrase table.
****************************************************/
void PrintPhrase(char *Phrases, int PhraseNum) {
int *Offsets;
char *p;
Offsets = (int *)Phrases;
p = Phrases+Offsets[PhraseNum];
while (p < Phrases + Offsets[PhraseNum + 1])
_putch(*p++);
}
/***************************************************
List all of the phrases if they're uncompressed.
If they're compressed, dump the hex/ascii.
****************************************************/
void PhraseDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
PHRASEHDR PhraseHdr;
int *Offsets, counter;
char *Phrases;
WORD DeCompSize;
/* Go to the phrases file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&PhraseHdr, sizeof(PhraseHdr), 1, HelpFile);
if (!TopicUse) printf("Phrase# - Phrase\n");
/* Allocate space and decompress if it's compressed, else read in. */
if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
if ((Offsets = malloc(PhraseHdr.PhrasesSize + (PhraseHdr.NumPhrases + 1) * 2)) == NULL) {
printf("No room to decompress |Phrases.");
return;
}
Phrases = Offsets + fread(Offsets, 2, PhraseHdr.NumPhrases+1, HelpFile);
DeCompSize = Decompress(HelpFile,
FileHdr.FileSize - (sizeof(PhraseHdr) + 2 * (PhraseHdr.NumPhrases+1)),
Phrases);
if (DeCompSize != PhraseHdr.PhrasesSize) {
printf("The amount of data after decompression does not match\n");
printf("the expected size from the Phrase Header!!!\n\n");
}
} /* if */
/* Else, no compression, so just read in the data */
else {
if ((Offsets = malloc(FileHdr.FileSize - sizeof(PhraseHdr))) == NULL) {
printf("No room to decompress |Phrases.");
return;
}
/* Backup 4 bytes for uncompressed Phrases (no PhrasesSize) */
fseek(HelpFile, -4, SEEK_CUR);
fread(Offsets, FileHdr.FileSize - 4, 1, HelpFile);
} /* else */
Phrases = (char *) Offsets;
PhrasesPtr = Phrases;
if (TopicUse) return;
for (counter=0; counter < PhraseHdr.NumPhrases; counter++) {
printf("\n%d - ", counter);
PrintPhrase(Phrases, counter);
}
free(Offsets);
}
/***************************************************
Lists all of the fonts and font descriptors.
Fonts are fixed length 20 followed by Font
descriptors of Fixed Length 11 bytes.
****************************************************/
void FontDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
FONTHEADER FontHdr;
FONTDESCRIPTOR FontDesc;
char AFont[20];
long FontStart, CurrLoc;
int counter;
/* Go to the FONT file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&FontHdr, sizeof(FontHdr), 1, HelpFile);
printf("|FONTS\n\n Number Fonts: %d\n",FontHdr.NumFonts);
printf("Font # - Font Name\n");
/* Keep track of start of fonts */
FontStart = ftell(HelpFile);
for (counter = 0; counter < FontHdr.NumFonts; counter++) {
fread(AFont, 20, 1, HelpFile);
printf(" %3d - %s\n", counter, AFont);
}
/* Go to Font Descriptors. Don't actually need this, because we're
there, but wanted to show how to get there using the offset. */
fseek(HelpFile, FontStart + (long)(FontHdr.DescriptorsOffset) - sizeof(FontHdr), SEEK_SET);
printf("\nNum Font Descriptors: %d\n", FontHdr.NumDescriptors);
printf("Default Descriptor: %d\n\n", FontHdr.DefDescriptor);
printf("Attributes: n=none b=bold i=ital u=undr s=strkout d=dblundr C=smallcaps\n\n");
printf("Font Name PointSize Family FG RGB BG RGB Attributes\n");
printf("--------------------------------------------------------------------------\n");
for (counter = 0; counter < FontHdr.NumDescriptors; counter++) {
fread(&FontDesc, sizeof(FontDesc), 1, HelpFile);
CurrLoc = ftell(HelpFile);
fseek(HelpFile, FontStart + (20L * FontDesc.FontName), SEEK_SET);
fread(AFont, 20, 1, HelpFile);
fseek(HelpFile, CurrLoc, SEEK_SET);
/* write out info on Font descriptor */
printf("%-20s %4.1f ", AFont, (float)(FontDesc.HalfPoints / 2));
switch (FontDesc.FontFamily) {
case FAM_MODERN: printf("Modern");
break;
case FAM_ROMAN: printf("Roman ");
break;
case FAM_SWISS: printf("Swiss ");
break;
case FAM_SCRIPT: printf("Script");
break;
case FAM_DECOR: printf("Decor ");
break;
default: printf("0X%02X ", FontDesc.FontFamily);
break;
} /* Switch */
printf(" 0X%08lX ",RGB(FontDesc.FGRGB[0], FontDesc.FGRGB[1], FontDesc.FGRGB[2]));
printf("0X%08lX ",RGB(FontDesc.BGRGB[0], FontDesc.BGRGB[1], FontDesc.BGRGB[2]));
if (FontDesc.Attributes == 0) _putch('n');
if (FontDesc.Attributes & FONT_BOLD) _putch('b');
if (FontDesc.Attributes & FONT_ITAL) _putch('i');
if (FontDesc.Attributes & FONT_UNDR) _putch('u');
if (FontDesc.Attributes & FONT_STRK) _putch('s');
if (FontDesc.Attributes & FONT_DBUN) _putch('d');
if (FontDesc.Attributes & FONT_SMCP) _putch('C');
printf("\n");
}
}
/***************************************************
Dumps the |TOMAP file. Simple list of topic
numbers and topic offsets.
****************************************************/
void ToMapDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
TOMAPHEADER ToMapHdr;
int counter;
/* Go to the TOMAP file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&ToMapHdr, 64, 1, HelpFile);
ToMapHdr.ToMapLen = (FileHdr.FileSize - (16 * 4)) / 4;
ToMapHdr.TopicPtr = malloc(ToMapHdr.ToMapLen);
fread(ToMapHdr.TopicPtr, ToMapHdr.ToMapLen, 1, HelpFile);
printf("Topic # - Topic Start\n");
for (counter=0; counter < ToMapHdr.ToMapLen; counter++)
printf("%3d - 0x%08lX\n", counter, ToMapHdr.TopicPtr[counter]);
free(ToMapHdr.TopicPtr);
}
/***************************************************
Because the topic file is broken into 4k blocks,
we'll have to handle all the reads. The main
idea is to filter out the TOPICBLOCKHEADERs and
do an decompression that needs doing.
****************************************************/
long TopicRead(BYTE *Dest, long NumBytes, FILE *HelpFile) {
static long CurrBlockLoc = 0; /* Where we are in the block */
static BYTE *DCmpBlock = NULL; /* Block of uncompressed data */
static long DecompSize; /* Size of block after decomp */
static long TopicStart, BlkNum; /* Start of |TOPIC file */
int BytesLeft; /* # Bytes left to return */
TOPICBLOCKHEADER BlockHeader;
TOPICLINK *TempLink;
long EndOffset;
/* If NumBytes = 0, then we're done and need to free memory */
if (NumBytes == -1) {
free(DCmpBlock);
return 0;
}
/* Is this our first time in? If so, allocate space needed */
if (!DCmpBlock) {
if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
/* MS assumes up to 75% compression. That's a bit */
/* optimistic given the algorithm. Expect more */
/* like 25%-30% at best. We'll go with 75% anyway */
DCmpBlock = malloc(4 * TopicBlockSize);
/* Get the current Topic location */
TopicStart = ftell(HelpFile);
/* We're at Block 0 */
BlkNum = 0;
DecompSize = 0; /* Set initial size to 0 */
if (!DCmpBlock) {
printf("Not enough memory to decompress |TOPIC file!\n");
return -1;
}
}
else {
DCmpBlock = malloc(TopicBlockSize); /* Space needed for decompressed block */
DecompSize = 0; /* Set initial size to zero */
if (!DCmpBlock) {
printf("Not enough memory to handle a |TOPIC file!\n");
return -1;
}
}
/* Don't really need the first block header, so get it out of the way */
fread(&BlockHeader, sizeof(BlockHeader), 1, HelpFile);
}
/* Get the info for the TopicRead */
BytesLeft = NumBytes;
while (BytesLeft) {
/* Do we need to read in a new block? */
if (DecompSize == CurrBlockLoc) {
/* Increment the block number */
BlkNum++;
/* If it's a compressed block, decompress it */
if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
DecompSize = Decompress(HelpFile, TopicBlockSize-1, DCmpBlock);
/* Align ourselves at next 4k block */
fseek(HelpFile, TopicStart + (4096L * BlkNum), SEEK_SET);
}
else
DecompSize = fread(DCmpBlock, 1, TopicBlockSize, HelpFile);
CurrBlockLoc = 0;
/* This is the tricky part! If the block isn't full, */
/* we need to find the end of it and change */
/* DecompSize to adjust to the real end. We do this */
/* by reading the block header of the next block */
/* and going to the last record in this block. */
fread(&BlockHeader, sizeof(BlockHeader), 1, HelpFile);
/* Get offset of last topic link. (Don't need the block #, hence 3FFFh) */
EndOffset = BlockHeader.LastTopicLink & 0x3FFF;
TempLink = (TOPICLINK *) (DCmpBlock + EndOffset - sizeof(BlockHeader) );
/* Actual end of the data (Don't include header) */
EndOffset += (TempLink->BlockSize - sizeof(BlockHeader));
/* If end is shorter than topic block, use it, otherwise topic block is full */
if (EndOffset > DecompSize) {
/* Adjust DecompSize if crossing 4k boundary */
EndOffset = TempLink->BlockSize - ((TempLink->NextBlock) & 0x3FFF);
DecompSize = (BlockHeader.LastTopicLink & 0x3FFF) + EndOffset;
}
else DecompSize = EndOffset;
} /* If */
*(Dest++) = *(DCmpBlock + (CurrBlockLoc++) );
BytesLeft--;
} /* While (BytesLeft) */
return NumBytes;
}
/***************************************************
Displays a string from a topic link record. Checks
for Phrase replacement and non-printable chars.
****************************************************/
void StringPrint(char *String, long Length) {
BYTE Byte1, Byte2;
int CurChar, PhraseNum;
int PostSpace;
long counter;
for(counter = 0; counter < Length; counter++) {
CurChar = * ((char *) (String + counter));
/* Check for Phrase replacement! */
if ((CurChar > 0) && (CurChar < 10)) {
Byte1 = CurChar;
counter++;
CurChar = * ((char *) (String + counter));
Byte2 = CurChar;
PhraseNum = (256 * (Byte1 - 1) + Byte2);
/* If there's a remainder, we have a space after the phrase */
PostSpace = PhraseNum % 2;
/* Divide Phrase number by 2 */
PhraseNum = PhraseNum / 2;
PrintPhrase(PhrasesPtr, PhraseNum);
if (PostSpace) _putch(' ');
}
else
if (isprint(CurChar)) putchar(CurChar);
else printf("(0x%02X)", (CurChar & 0x00FF));
}
}
/***************************************************
List all of the phrases if they're uncompressed.
If they're compressed, dump the hex/ascii.
****************************************************/
void TopicDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
TOPICHEADER *TopicHdr;
TOPICLINK TopicLink;
/* Go to the TOPIC file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
do {
TopicRead((BYTE *) &TopicLink, sizeof(TopicLink) - 4, HelpFile);
/* TopicLink.DataLen2 assumes you are doing Phrase replacement. */
/* Since we're not, we don't know the length of the phrases that */
/* would be replaced. Therefore we're going to modify the value */
/* of DataLen2 to reflect the value without phrase replacement. */
if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN))
TopicLink.DataLen2 = TopicLink.BlockSize - TopicLink.DataLen1;
/* 21=sizeof(TopicLink) - LinkData1 & LinkData2 fields. */
TopicLink.LinkData1 = malloc(TopicLink.DataLen1 - 21);
if(!TopicLink.LinkData1) {
printf("Error allocating TopicLink.LinkData1!\n");
return;
}
TopicRead(TopicLink.LinkData1, TopicLink.DataLen1 - 21, HelpFile);
if (TopicLink.DataLen2 > 0) {
TopicLink.LinkData2 = malloc(TopicLink.DataLen2 + 1);
if(!TopicLink.LinkData2) {
printf("Error allocating TopicLink.LinkData2!\n");
return;
}
TopicRead(TopicLink.LinkData2, TopicLink.DataLen2, HelpFile);
}
/* Display a Topic Header record */
if (TopicLink.RecordType == TL_TOPICHDR) {
TopicHdr = (TOPICHEADER *)TopicLink.LinkData1;
printf("========================================================\n");
printf("Topic#: %ld Block Size: %ld\n",
TopicHdr->TopicNum,
TopicHdr->BlockSize);
if (TopicLink.DataLen2 > 0)
StringPrint(TopicLink.LinkData2, TopicLink.DataLen2);
else printf("\n");
}
/* Show a 'text' type record. */
else if (TopicLink.RecordType == TL_DISPLAY) {
printf("--------------------------------------------------------\n");
printf("Block Size: %ld\n", TopicLink.BlockSize);
StringPrint(TopicLink.LinkData2, TopicLink.DataLen2);
}
printf("\n\n");
free(TopicLink.LinkData1);
if (TopicLink.DataLen2 > 0) free(TopicLink.LinkData2);
} while(TopicLink.NextBlock != -1);
}
/***************************************************
Get First leaf. Used by TTLDump ,KWBTreeDump and
ContextDump.
****************************************************/
int OtherGetFirstLeaf(FILE *HelpFile, long FirstPageLoc, int LeafLevel) {
int CurrLevel = 1;
BTREEINDEXHEADER CurrNode;
int NextPage = 0;
while(CurrLevel < LeafLevel) {
fread(&CurrNode, 4, 1, HelpFile);
fread(&NextPage, sizeof(int), 1, HelpFile);
GotoPage(NextPage);
CurrLevel++;
}
return NextPage;
}
/***************************************************
Dumps the Topic Titles B-Tree
****************************************************/
void TTLDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
char Title[80], c;
int count, Index;
long CurrPage, FirstPageLoc, TopicOffset;
BTREEHEADER BTreeHdr;
BTREENODEHEADER CurrNode;
/* Go to the TTLBTREE file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
/* Save the current location */
FirstPageLoc = ftell(HelpFile);
GotoPage(BTreeHdr.RootPage);
printf("# Titles in |TTLBTREE %lu\n\n", BTreeHdr.TotalBtreeEntries);
CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
do {
GotoPage(CurrPage);
fread(&CurrNode, 8, 1, HelpFile);
for(count = 1; count <= CurrNode.NEntries; count++) {
fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
Index = 0;
while(c = fgetc(HelpFile))
Title[Index++] = c;
Title[Index] = 0;
printf("Topic Offset:0x%08lX Title: %s\n", TopicOffset, Title);
}
CurrPage = CurrNode.NextPage;
} while(CurrPage != -1);
}
/***************************************************
Dumps the Keyword Map file
****************************************************/
void KWMapDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
WORD NumKWMaps, count;
KWMAPREC KeywordMap;
/* Go to the KWMAP file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&NumKWMaps, sizeof(NumKWMaps), 1, HelpFile);
for(count = 1; count <= NumKWMaps; count++) {
fread(&KeywordMap, sizeof(KWMAPREC), 1, HelpFile);
printf("Record:%05u First Keyword:0x%08lX Leaf Page#:%05u\n",
count, KeywordMap.FirstRec, KeywordMap.PageNum);
}
}
/***************************************************
Dumps the Keyword data file
****************************************************/
void KWDataDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
WORD NumKWLocs, count;
long TopicOffset;
/* Go to the KWDATA file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
NumKWLocs = FileHdr.FileSize / 4;
printf("Number of Keyword Occurrances: %5u\n\n", NumKWLocs);
for(count = 1; count <= NumKWLocs; count++) {
fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
printf("Occurance:%05u Topic Offset:0x%08lX\n",
count, TopicOffset);
}
}
/***************************************************
Dumps the keyword B-Tree file
****************************************************/
void KWBTreeDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
char c;
int count, Index;
long CurrPage, FirstPageLoc;
BTREEHEADER BTreeHdr;
BTREENODEHEADER CurrNode;
KWBTREEREC KWBRec;
/* Go to the KWBTREE file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
/* Save the current location */
FirstPageLoc = ftell(HelpFile);
GotoPage(BTreeHdr.RootPage);
printf("# Keywords in |KWBTREE %lu\n\n", BTreeHdr.TotalBtreeEntries);
CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
do {
GotoPage(CurrPage);
fread(&CurrNode, 8, 1, HelpFile);
for(count = 1; count <= CurrNode.NEntries; count++) {
Index = 0;
while(c = fgetc(HelpFile))
KWBRec.Keyword[Index++] = c;
KWBRec.Keyword[Index] = 0;
fread(&KWBRec.Count, sizeof(int), 1, HelpFile);
fread(&KWBRec.KWDataOffset, sizeof(long), 1, HelpFile);
printf("KWData Offset:0x%08lX # Offsets:%05d Keyword: %s\n",
KWBRec.KWDataOffset, KWBRec.Count, KWBRec.Keyword);
}
CurrPage = CurrNode.NextPage;
} while(CurrPage != -1);
}
/***************************************************
Dumps the Context file.
****************************************************/
void ContextDump(FILE *HelpFile, long FileStart) {
int count;
long CurrPage, FirstPageLoc;
BTREEHEADER BTreeHdr;
BTREENODEHEADER CurrNode;
FILEHEADER FileHdr;
CONTEXTREC ContextRec;
/* Go to the CONTEXT file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
/* Save the current location */
FirstPageLoc = ftell(HelpFile);
GotoPage(BTreeHdr.RootPage);
printf("# Values in hash table:%lu\n\n", BTreeHdr.TotalBtreeEntries);
CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
do {
GotoPage(CurrPage);
fread(&CurrNode, 8, 1, HelpFile);
for(count = 1; count <= CurrNode.NEntries; count++) {
fread(&ContextRec, sizeof(ContextRec), 1, HelpFile);
printf("Hash Value:%12ld Topic Offset:0x%08lX\n",
ContextRec.HashValue, ContextRec.TopicOffset);
}
CurrPage = CurrNode.NextPage;
} while(CurrPage != -1);
}
/***************************************************
Dump the Context map file. Real simple.
****************************************************/
void CTXOMAPDump(FILE *HelpFile, long FileStart) {
FILEHEADER FileHdr;
CTXOMAPREC CTXORec;
WORD NumRecs, count;
/* Go to the CTXOMAP file and get the headers */
fseek(HelpFile, FileStart, SEEK_SET);
fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
fread(&NumRecs, sizeof(NumRecs), 1, HelpFile);
printf("Number of Context Mapping records: %u\n\n", NumRecs);
for (count=1; count <= NumRecs; count++) {
fread(&CTXORec, sizeof(CTXORec), 1, HelpFile);
printf("Record:%05u Map ID:0x%08lX Topic Offset:0x%08lX\n",
count, CTXORec.MapID, CTXORec.TopicOffset);
}
}
/***************************************************
Dumps a given WHIFS file. First thing it does,
though, is loads the system rec. This is needed
to check if |Phrases or |TOPIC are compressed.
After that, it just finds the file and dumps it.
****************************************************/
void DumpFile(FILE *HelpFile) {
WORD FileCounter;
char FileName[20];
long FileOffset, PhrasesOffset;
/* Load WHIFSHeader & First Leaf */
WHIFSGetFirstLeaf(HelpFile);
/* Loads the system record into memory. Need it */
/* to determine if compression is used. */
TopicUse = 0;
if ((!strcmp(WHIFSFileToRead, "TOPIC")) || (!strcmp(WHIFSFileToRead, "|TOPIC")))
TopicUse = 1;
for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
GetFile(HelpFile, FileCounter, &FileOffset, FileName);
if (!strcmp(FileName, "|SYSTEM"))
SysLoad(HelpFile, FileOffset);
if ((!strcmp(FileName, "|Phrases")) && (TopicUse))
PhrasesOffset = FileOffset;
}
/* Load Phrases for |TOPIC dump */
if (TopicUse) PhraseDump(HelpFile, PhrasesOffset);
/* Find the file the user wants to dump */
for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
GetFile(HelpFile, FileCounter, &FileOffset, FileName);
if (!strcmp(FileName, WHIFSFileToRead)) break;
}
if (FileCounter == WHIFSHeader.TotalWHIFSEntries) {
/* try appending a | to the filename. */
strcpy(FileName, "|");
strcat(FileName, WHIFSFileToRead);
strcpy(WHIFSFileToRead, FileName);
for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
GetFile(HelpFile, FileCounter, &FileOffset, FileName);
if (!strcmp(FileName, WHIFSFileToRead)) break;
}
}
/* Bad file! */
if (FileCounter == WHIFSHeader.TotalWHIFSEntries) {
printf("File %s not found in help file.\n", WHIFSFileToRead);
return;
}
if (!strcmp(FileName, "|SYSTEM"))
SysDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|Phrases"))
PhraseDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|FONT"))
FontDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|TOMAP"))
ToMapDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|TOPIC"))
TopicDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|TTLBTREE"))
TTLDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|KWMAP"))
KWMapDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|KWDATA"))
KWDataDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|KWBTREE"))
KWBTreeDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|CONTEXT"))
ContextDump(HelpFile, FileOffset);
else if (!strcmp(FileName, "|CTXOMAP"))
CTXOMAPDump(HelpFile, FileOffset);
else
HexDump(HelpFile, FileOffset);
}
/***************************************************
Read the Help Header then build the WHIFS B-Tree.
Show the WHIFS directory to the user and see what
they want to do. (See a file or quit?)
****************************************************/
void HelpDump(FILE *HelpFile) {
fread(&HelpHeader, sizeof(HelpHeader), 1, HelpFile);
if (HelpHeader.MagicNumber != 0x000000035F3F) {
printf("Fatal Error:\n");
printf(" Not a valid WinHelp file!\n");
return;
}
if (ReadWHIFSFile)
DumpFile(HelpFile);
else
ListFiles(HelpFile);
}
/***************************************************
Show usage.
****************************************************/
void Usage() {
printf("Usage:\n");
printf(" HELPDUMP helpfile[.hlp] [WHIFSFilename]\n\n\n");
printf(" helpfile - Name of help file (.HLP or .MVB)\n");
printf(" WHIFSFilename - Name of internal file to view\n\n");
printf(" If only a helpfile is provided, the WHIFS directory is shown.\n");
printf(" Provide a WHIFSFilename to display an internal file.\n");
}
/***************************************************
Open the file and dump it.
****************************************************/
int main(int argc, char *argv[]) {
char filename[40];
FILE *HelpFile;
if (argc < 2) {
Usage();
return EXIT_FAILURE;
}
ReadWHIFSFile = 0;
if (argc == 3) {
strcpy(WHIFSFileToRead, argv[2]);
ReadWHIFSFile = 1;
}
strcpy(filename, argv[1]);
_strupr(filename);
if (!strchr(filename, '.'))
strcat(filename, ".HLP");
if ((HelpFile = fopen(filename, "rb")) == NULL) {
printf("%s does not exist!", filename);
return EXIT_FAILURE;
}
HelpDump(HelpFile);
fclose(HelpFile);
return EXIT_SUCCESS;
}